home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume2 / printers / lwf.1
Text File  |  1988-11-10  |  57KB  |  2,267 lines

  1. Path: xanth!nic.MR.NET!tank!mimsy!dftsrv!ukma!mailrus!ulowell!page
  2. From: page@swan.ulowell.edu (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v02i058:  lwf - ascii to postscript filter
  5. Message-ID: <10119@swan.ulowell.edu>
  6. Date: 10 Nov 88 02:12:10 GMT
  7. Organization: University of Lowell, Computer Science Dept.
  8. Lines: 2256
  9. Approved: page@swan.ulowell.edu
  10.  
  11. Submitted-by: paolucci@snll-arpagw.llnl.gov (Sam Paolucci)
  12. Posting-number: Volume 2, Issue 58
  13. Archive-name: printers/lwf.1
  14.  
  15. Lwf converts ASCII text files to PostScript.  Most of the processing
  16. is done on the host rather than on the printer.  The name "lwf",
  17. perhaps poorly chosen, stands for "LaserWriter Filter".
  18.  
  19. #    This is a shell archive.
  20. #    Remove everything above and including the cut line.
  21. #    Then run the rest of the file through sh.
  22. #----cut here-----cut here-----cut here-----cut here----#
  23. #!/bin/sh
  24. # shar:    Shell Archiver
  25. #    Run the following text with /bin/sh to create:
  26. #    Makefile
  27. #    README
  28. #    README2
  29. #    ehandler.ps
  30. #    lwf.c
  31. #    lwf.cfg
  32. #    lwf.man
  33. #    lwf.prologue
  34. #    psclear.uu
  35. #    range.c
  36. # This archive created: Wed Nov  9 21:07:28 1988
  37. cat << \SHAR_EOF > Makefile
  38. # cc flags
  39. CFLAGS = +fi +L
  40.  
  41. # Where to put the PostScript prologue file
  42. PROLOGUE = S:lwf.prologue
  43.  
  44. # Environment options:
  45. #
  46. # -DAMIGA    Since we don't have the passwd information
  47. # -DPROLOGUE="$(PROLOGUE)"
  48. # -DPR=\"where pr is, e.g. /bin/pr\"
  49. # -DSYSV    For System V (assumes SYSV-style pr)
  50. # -DREVERSE=0    Set to non-zero if page reversal is to be the default
  51. # -DHOSTNAME=\"yourhostname\"    If you don't have the hostname() system call
  52. ENV = -DREVERSE=1 -DAMIGA
  53.  
  54. lwf: lwf.o range.o
  55.     ln -o lwf lwf.o range.o -lsock32 -lma32 -lc32
  56.  
  57. lwf.o: lwf.c
  58.     cc $(CFLAGS) $(ENV) lwf.c
  59.  
  60. range.o: range.c
  61.     cc $(CFLAGS) range.c
  62.  
  63. clean:
  64.     delete lwf.o range.o
  65. SHAR_EOF
  66. cat << \SHAR_EOF > README
  67.                 NOTES
  68.                 -----
  69.  
  70. Recently I ran into a posting of lwf in one of the UNIX groups on
  71. USENET.  I ported it to the Amiga since I had a real need for
  72. something like this.  It is relatively full featured, and I added a
  73. couple of additional options myself and fixed a few things.  With
  74. appropriate mods to the makefile the code should compile and run fine
  75. on AmigaDOS as well as on UNIX machines (I used Manx).  To recompile
  76. on the Amiga you will need Ameristar's socket library. 
  77.  
  78. On the Amiga it will look for environment variables USER and HOSTNAME
  79. which will be printed on the header page along with the date and file
  80. names.  The environment variables are as used by Manx and ARP and can
  81. be set using the set command.  If they are not set, the fields will be
  82. printed blank. 
  83.  
  84. The program generates a PostScript prologue file which is identical to
  85. lwf.prologue.  If you want to use your own or a modified version of
  86. lwf.prologue, you need to use the -P command line option. 
  87.  
  88. If you have difficulty in printing a large file, it may be because the
  89. default is to print the pages in reverse order.  To accomplish this
  90. feat, a temporary file is created in RAM: and deleted afterwards.  For
  91. a large file this will require a corresponding amount of memory.  If
  92. this is your problem, you can easily get around it by turning off the
  93. page reversal with the -r flag. 
  94.  
  95. I'm hoping to tie this code to a print spooler running over an
  96. Ethernet network.  When that happens I'll post it.  In the meantime
  97. the -p flag that appears in the manual page is not currently
  98. implemented on the Amiga. 
  99.  
  100. Defaults of all command line arguments can be specified in the
  101. configuration file lwf.cfg which should be placed in S:.  An example
  102. file is enclosed.  This file consists of lines in which the initial
  103. character in each line specifies the command.  If the initial
  104. character is a space, an asterisk, a pound sign, a semicolon, or a
  105. line feed, the line is ignored.  This makes it useful for commenting
  106. the file.  White space following the command is optional.  Comments
  107. can also be placed on the individual lines after the complete command
  108. is specified, as shown in the example. 
  109.  
  110. Also included in the directory are two useful files.  If you are lucky
  111. enough to have a PostScript laser printer tied directly to your Amiga,
  112. you should download ehandler.ps into the printer once and only once,
  113. preferably from within your startup-sequence.  In the case you should
  114. send a file to the printer which contains an error, this file, which
  115. is an official Adobe error handler, will generate an error
  116. description.  The second file, psclear, should be downloaded into the
  117. laser printer when the printer seems to just be waiting for more
  118. information AND you have not used ehandler.ps.  This file will send an
  119. end-of-file to the laser printer. 
  120.  
  121.                     Enjoy. 
  122.  
  123. Dr. Samuel Paolucci
  124. 1351 Roselli Dr.
  125. Livermore, CA 94550
  126. (415)294-2018
  127.  
  128. ARPA: paolucci@snll-arpagw.llnl.gov
  129. SHAR_EOF
  130. cat << \SHAR_EOF > README2
  131.  
  132. Lwf converts ASCII text files to PostScript.
  133. It has been tested on the Apple LaserWriter/LaserWriter+ and the NEC
  134. Silentwriter LC-890.  Most of the processing is done on the host
  135. rather than on the printer.  The name "lwf", perhaps poorly chosen, stands
  136. for "LaserWriter Filter".
  137.  
  138. There may be a couple of minor Berkeley-isms in the program (see the
  139. routine setup()) and it is somewhat Unix dependent (eg., popen(), /bin/pr
  140. are expected).  The output conforms to the Adobe 2.0 file structuring
  141. conventions.  The program is set up to use the (fixed-width) Courier font.
  142. If you use another font you'll have to modify the mapping table in lwf.c.
  143. I can't recommend a proportional font for this purpose.
  144.  
  145. The distribution includes the following files:
  146.  
  147. README2        This file
  148. Makefile
  149. lwf.c        LaserWriter filter to convert ASCII text to PostScript
  150. range.c        Routine to check if a number is in a given range
  151. lwf.1.man    Manual page for lwf
  152. lwf.prologue    Default PostScript support file
  153.  
  154. Please check the Makefile and the configurable #defines in lwf.c.
  155. Check if the man page needs to be revised for your environment.
  156. You can then proceed with 'make install'.
  157.  
  158. If you come across any bugs, including nonconformance to Adobe 2.0, or
  159. make any changes you'd like to share please send mail to me rather than
  160. posting to the net.
  161.  
  162. Enjoy.
  163.  
  164. -----
  165. Barry Brachman           | {ihnp4!alberta,uw-beaver,uunet}!
  166. Dept. of Computer Science|  ubc-vision!ubc-csgrads!brachman
  167. Univ. of British Columbia| brachman@grads.cs.ubc.cdn
  168. Vancouver, B.C. V6T 1W5  | brachman%ubc.csnet@csnet-relay.arpa
  169. (604) 228-4327           | brachman@ubc.csnet
  170.  
  171. SHAR_EOF
  172. cat << \SHAR_EOF > ehandler.ps
  173. %!PS-Adobe-2.0
  174. % ehandler.ps -- Downloaded Error Break-page handler
  175. % Copyright (c) 1984, 1985, 1986  Adobe Systems Incorporated.
  176. % All Rights Reserved.
  177.  
  178. 0000 % exitserver password
  179. /$brkpage where { %ifelse
  180.     pop pop
  181.     (Error Handler in place - not loaded again\n)
  182.     print flush stop
  183. }{ %else
  184.     dup serverdict begin
  185.     statusdict begin checkpassword { %ifelse
  186.         (Error Handler downloaded.\n)print flush
  187.         exitserver
  188.     }{ %else
  189.         pop
  190.         (Bad Password on loading error handler!!!\n)
  191.         print flush stop
  192.     } ifelse
  193. } ifelse
  194. /$brkpage 64 dict def $brkpage begin
  195. /prnt { %def
  196.     dup type /stringtype ne { =string cvs } if
  197.     dup length 6 mul
  198.     /tx exch def /ty 10 def
  199.     currentpoint /toy exch def /tox exch def
  200.     1 setgray newpath
  201.     tox toy 2 sub moveto
  202.     0 ty rlineto tx 0 rlineto
  203.     0 ty neg rlineto
  204.     closepath fill
  205.     tox toy moveto 0 setgray show
  206. } bind def
  207. /nl { %def
  208.     currentpoint exch pop lmargin exch moveto
  209.     0 -10 rmoveto
  210. } def
  211. /== { /cp 0 def typeprint nl } def
  212. /typeprint {
  213.     dup type dup currentdict exch known {exec}{
  214.         unknowntype
  215.     } ifelse
  216. } readonly def
  217. /lmargin 72 def /rmargin 72 def
  218. /tprint { %def
  219.     dup length cp add rmargin gt { nl /cp 0 def } if
  220.     dup length cp add /cp exch def
  221.     prnt
  222. } readonly def
  223. /cvsprint { =string cvs tprint ( ) tprint } readonly def
  224. /unknowntype { %def
  225.     exch pop cvlit (??) tprint cvsprint
  226. } readonly def
  227. /integertype { cvsprint } readonly def
  228. /realtype { cvsprint } readonly def
  229. /booleantype { cvsprint } readonly def
  230. /operatortype { (//) tprint cvsprint } readonly def
  231. /marktype { pop (-mark- ) tprint } readonly def
  232. /dicttype { pop (-dictionary- ) tprint } readonly def
  233. /nulltype { pop (-null- ) tprint } readonly def
  234. /filetype { pop (-filestream- ) tprint } readonly def
  235. /savetype { pop (-savelevel- ) tprint } readonly def
  236. /fonttype { pop (-fontid- ) tprint } readonly def
  237. /nametype { %def
  238.     dup xcheck not { (/) tprint } if cvsprint
  239. } readonly def
  240. /stringtype { %def
  241.     dup rcheck { %ifelse
  242.         (\() tprint tprint (\)) tprint
  243.     }{ %else
  244.         pop (-string- ) tprint
  245.     } ifelse
  246. }readonly def
  247. /arraytype { %def
  248.     dup rcheck { %ifelse
  249.         dup xcheck { %ifelse
  250.             ({) tprint { typeprint } forall (}) tprint
  251.         }{ %else
  252.             ([) tprint { typeprint } forall (]) tprint
  253.         } ifelse
  254.     }{ %else
  255.         pop (-array- ) tprint
  256.     } ifelse
  257. } readonly def
  258. /packedarraytype { %def
  259.     dup rcheck { %ifelse
  260.         dup xcheck { %ifelse
  261.             ({) tprint { typeprint } forall (}) tprint
  262.         }{ %else
  263.             ([) tprint { typeprint } forall (]) tprint
  264.         } ifelse
  265.     }{ %else
  266.         pop (-packedarray- ) tprint
  267.     } ifelse
  268. } readonly def
  269. /courier /Courier findfont 10 scalefont def
  270. /OLDhandleerror errordict /handleerror get def
  271. end %$brkpage
  272.  
  273. /handleerror { %put
  274.     systemdict begin $error begin $brkpage begin
  275.     newerror { %ifelse
  276.         /newerror false store
  277.         vmstatus pop pop 0 ne { grestoreall } if
  278.         initgraphics courier setfont
  279.         lmargin 720 moveto (ERROR: ) prnt
  280.         errorname prnt
  281.         nl (OFFENDING COMMAND: ) prnt
  282.         /command load prnt
  283.         $error /ostack known { %if
  284.             nl nl (STACK:) prnt nl nl
  285.             $error /ostack get aload length { == } repeat
  286.         } if
  287.         systemdict /showpage get exec
  288.         /newerror true store
  289.         /OLDhandleerror load end end end exec
  290.     }{ %else
  291.         end end end
  292.     } ifelse
  293. }
  294. dup 0 systemdict put   % replace name by actual dict object
  295. dup 4 $brkpage put     % replace name by dict object
  296. bind readonly
  297.  
  298. errordict 3 1 roll put % put proc in errordict as /handleerror
  299. SHAR_EOF
  300. cat << \SHAR_EOF > lwf.c
  301.  
  302. /* vi: set tabstop=4 : */
  303.  
  304. /*
  305.  * lwf - Convert ASCII text to PostScript
  306.  *
  307.  * Usage: lwf [-c#] [-d] [-i#] [-l] [-m] [-n] [-olist] [-p[str]] [-P filename]
  308.  *            [-r] [-s#] [-t#] [-v] [-w] [-S] [file ...]
  309.  *
  310.  *
  311.  * Options:
  312.  *    -c#        Number of copies
  313.  *    -d        Debug mode
  314.  *    -i#        Indent each line # inches (so much for metric)
  315.  *    -l        Landscape instead of Portrait
  316.  *    -m        Use 3 hole punch margins
  317.  *    -n        Leave top and bottom margin (66 lines/page at 10pt)
  318.  *    -olist        Only print pages in the specified range
  319.  *    -p[str]        Use pr to print, passing optional string
  320.  *    -P filename    Copy prologue from filename instead of default
  321.  *    -r        Toggle page reversal flag (see Makefile)
  322.  *    -s#        Use point size #
  323.  *    -t#        Spaces between tab stops is # characters
  324.  *    -v        Verbose
  325.  *    -w        Line wrap (not implemented yet)
  326.  *    -S        Standalone mode (print header page, use EOF's)
  327.  *
  328.  * If no files are specified, stdin is used.
  329.  * Form feeds handled.
  330.  * Backspacing with underlining (or overprinting) works.
  331.  * The output conforms to Adobe 2.0.
  332.  *
  333.  * Problems:
  334.  *    - assumes fixed-width (non-proportional) font in some places
  335.  *    - can't back up (using backspaces) over tabs
  336.  *    - assumes 8.5 x 11.0 paper
  337.  *
  338.  * BJB - Jun/87
  339.  * SP  - May/88
  340.  * ========================================================================
  341.  *
  342.  * Permission is given to freely copy and distribute this software provided:
  343.  *
  344.  *    1) You do not sell it,
  345.  *    2) You do not use it for commercial advantage, and
  346.  *    3) This notice accompanies the distribution
  347.  *
  348.  * Copyright (c) 1988
  349.  * Barry Brachman
  350.  * Dept. of Computer Science
  351.  * Univ. of British Columbia
  352.  * Vancouver, B.C. V6T 1W5
  353.  *
  354.  * .. {ihnp4!alberta, uw-beaver, uunet}!ubc-vision!ubc-csgrads!brachman
  355.  * brachman@grads.cs.ubc.cdn
  356.  * brachman%ubc.csnet@csnet-relay.arpa
  357.  * brachman@ubc.csnet
  358.  * ========================================================================
  359.  */
  360.  
  361. #ifndef AMIGA
  362. #include <pwd.h>
  363. #endif AMIGA
  364. #include <ctype.h>
  365. #include <stdio.h>
  366.  
  367. #define min(a, b)        ((a) < (b) ? (a) : (b))
  368.  
  369. /*
  370.  * Configurable...
  371.  * BUFOUT should be fairly large
  372.  */
  373. #define BUFIN                1024    /* maximum length of an input line */
  374. #define BUFOUT                (BUFIN * 5)
  375. #define MAXPAGES            10000    /* maximum number of pages per job */
  376. #define DEFAULT_TAB_SIZE    8
  377. #define DEFAULT_POINT_SIZE    10
  378. #ifndef REVERSE
  379. #define REVERSE            0
  380. #endif REVERSE
  381.  
  382. #ifdef SYSV
  383. #define rindex            strrchr
  384. #endif SYSV
  385.  
  386. /*
  387.  * As mentioned in the man page, /bin/pr doesn't handle formfeeds correctly
  388.  * when doing multicolumn formatting.
  389.  * Instead of starting a new column or page it passes a formfeed through,
  390.  * causing pr and lwf to get out-of-synch with regard to the current
  391.  * location on the page.
  392.  * If your pr behaves this way (4.[23] does, SYSV doesn't), define PRBUG so
  393.  * that fgetline() will filter out these bogus formfeeds while preserving
  394.  * the column structuring.
  395.  */
  396. #ifndef SYSV
  397. #ifdef PR
  398. #define PRBUG                1
  399. #endif PR
  400. #endif SYSV
  401.  
  402. /*
  403.  * PostScript command strings defined in the prologue file
  404.  */
  405. #define BACKSPACE        "B"
  406. #define ENDPAGE            "EP"
  407. #define LINETO            "L"
  408. #define MOVETO            "M"
  409. #define NEWPATH            "NP"
  410. #define SHOW            "S"
  411. #define STARTPAGE        "SP"
  412. #define STARTHPAGE        "SHP"
  413. #define STARTLPAGE        "SLP"
  414. #define STROKE            "ST"
  415. #define TAB            "T"
  416.  
  417. /*
  418.  * Conformance requires that no PostScript line exceed 256 characters
  419.  */
  420. #define MAX_OUTPUT_LINE_LENGTH    256
  421.  
  422. #define TEXTFONT        "Courier"
  423. #define HEADERFONT        "Times-Roman"
  424. #define HEADERPS        18    /* header page point size */
  425.  
  426. #define PORTRAIT_START_Y    767    /* first row (Y coord) on each page */
  427. #define LANDSCAPE_START_Y    587    /* first row (Y coord) on each page */
  428. #define PORTRAIT_START_X    25    /* position of start of each line */
  429. #define LANDSCAPE_START_X    25    /* position of start of each line */
  430. #define START_X_HEADER        100    /* x start of each header line */
  431. #define START_Y_HEADER        700    /* first row (Y coord) of header */
  432. #define THREE_HOLE_X        1.0    /* portrait x offset (inches) 3 hole */
  433. #define THREE_HOLE_Y        0.5    /* landscape y offset (inches) 3 hole */
  434. #define MARGIN            41    /* top and bottom margin (points) */
  435.  
  436. #define MAX_X            612
  437. #define MAX_Y            792
  438.  
  439. #define SEP_CHAR        '\001'    /* pr column separator character */
  440.  
  441. #define PS_EOF            04
  442.  
  443. #define NPSIZES            6
  444. struct psize {
  445.     int size;            /* point size */
  446.     double charsperinch;        /* approx. char width, for Courier */
  447.     int portrait_page_length;    /* page length in lines */
  448.     int portrait_cols;        /* maximum # of chars per line */
  449.     int landscape_page_length;
  450.     int landscape_cols;
  451. } psize[NPSIZES] = {
  452.      7, 17.0, 106, 133, 80, 175,
  453.      8, 15.0,  93, 117, 70, 155,
  454.      9, 14.0,  82, 109, 62, 144,
  455.     10, 12.0,  74,  94, 56, 124,
  456.     11, 11.0,  67,  86, 51, 113,
  457.     12, 10.0,  62,  78, 47, 103
  458. };
  459.  
  460. #ifdef PR
  461. #define USAGE    \
  462. "[-c#] [-d] [-i#] [-l] [-m] [-n] [-olist] [-p[str]] [-P filename] [-r] [-s#] [-S] [-t#] [-v] [file ...]"
  463. #else PR
  464. #define USAGE    \
  465. "[-c#] [-d] [-i#] [-l] [-m] [-n] [-olist] [-P filename] [-r] [-s#] [-S] [-t#] [-v] [file ...]"
  466. #endif PR
  467.  
  468. struct psize *p, *get_psize();
  469. long page_map[MAXPAGES];    /* offset of first byte of each page */
  470. int page_count;
  471. int page;
  472.  
  473. int lines_per_page;
  474. int columns;
  475. int point_size;
  476. int start_x, start_y;
  477. int ncopies;
  478. int margin;
  479. double offset;
  480.  
  481. char bufin[BUFIN];        /* input buffer */
  482. char bufout[BUFOUT];        /* used for page reversal and output buffering */
  483.  
  484. #ifdef AMIGA
  485. char username[32];
  486. #else AMIGA
  487. char *username;
  488. #endif AMIGA
  489. char hostname[32];
  490. char tmpname[32];
  491. char *currentdate;
  492.  
  493. int row;
  494. char *range;
  495. int tabstop;
  496. char *propts;
  497.  
  498. int dflag, lflag, mflag, nflag, pflag, rflag, vflag, wflag, Sflag;
  499.  
  500. FILE *freopen();
  501. char *fgets();
  502. char *strcpy();
  503. char *fgetline();
  504.  
  505. char *prologue;
  506. char *progname;
  507. char *configfile = "s:lwf.cfg";
  508.  
  509. char *version = "lwf V2.0a 30-May-88";
  510.  
  511. main(argc, argv)
  512. int argc;
  513. char **argv;
  514. {
  515.     register int i, j, first_file;
  516.     char *pc;
  517.     double offset, bsize, atof();
  518.     int margin;
  519.     char *rindex();
  520.     FILE *infile, *popen();
  521.     double ceil(), floor();
  522.  
  523.     if ((pc = rindex(argv[0], '/')) != (char *) NULL)
  524.         progname = pc + 1;
  525.     else
  526.         progname = argv[0];
  527.  
  528.     init();
  529.  
  530.         getdefaults();
  531.  
  532.     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
  533.         switch (argv[i][1]) {
  534.         case 'c':
  535.             ncopies = atoi(&argv[i][2]);
  536.             if (ncopies <= 0) {
  537.                 fatal("number of copies must be > 0");
  538.                 /*NOTREACHED*/
  539.             }
  540.             break;
  541.         case 'd':
  542.             dflag = 1;
  543.             break;
  544.         case 'i':
  545.             offset = atof(&argv[i][2]);
  546.             if (offset < 0.0 || offset >= 8.5) {
  547.                 fatal("bad indent");
  548.                 /*NOTREACHED*/
  549.             }
  550.             break;
  551.         case 'l':
  552.             lflag = 1;
  553.             break;
  554.         case 'm':
  555.             mflag = 1;
  556.             break;
  557.         case 'n':
  558.             nflag = 1;
  559.             break;
  560.         case 'o':
  561.             range = &argv[i][2];
  562.             if (checkrange(range)) {
  563.                 fatal("bad range specification");
  564.                 /*NOTREACHED*/
  565.             }
  566.             break;
  567. #ifdef PR
  568.         case 'p':
  569.             pflag = 1;
  570.             propts = &argv[i][2];
  571.             break;
  572. #endif PR
  573.         case 'P':
  574.             if (++i == argc) {
  575.                 fatal("missing filename after -P");
  576.                 /*NOTREACHED*/
  577.             }
  578.             prologue = argv[i];
  579.             break;
  580.         case 'r':
  581.             rflag = !rflag;
  582.             break;
  583.         case 's':
  584.             j = atoi(&argv[i][2]);
  585.             if ((p = get_psize(j)) == (struct psize *) NULL) {
  586.                 fatal("bad point size");
  587.                 /*NOTREACHED*/
  588.             }
  589.             break;
  590.         case 't':
  591.             tabstop = atoi(&argv[i][2]);
  592.             if (tabstop < 1) {
  593.                 fatal("bad tabstop");
  594.                 /*NOTREACHED*/
  595.             }
  596.             break;
  597.         case 'v':
  598.             vflag = 1;
  599.             break;
  600.         case 'w':
  601.             wflag = 1;
  602.             break;
  603.         case 'S':
  604.             Sflag = 1;
  605.             break;
  606.         default:
  607.             (void) fprintf(stderr, "Usage: %s %s\n", progname, USAGE);
  608.             exit(1);
  609.         }
  610.     }
  611.  
  612.     /*
  613.      * Check that all files are readable.
  614.      * This is so that no output at all is produced if any file is not
  615.      * readable in case the output is being piped to a printer.
  616.      */
  617.     for (j = i; j < argc; j++) {
  618.         if (access(argv[j], 4) == -1) {
  619.             fatal("cannot access %s", argv[j]);
  620.             /*NOTREACHED*/
  621.         }
  622.     }
  623.  
  624.     point_size = p->size;
  625.  
  626.     if (nflag) {
  627.         margin = MARGIN;
  628.         bsize = MAX_Y - 2 * (MAX_Y - PORTRAIT_START_Y + MARGIN);
  629.         p->portrait_page_length = (int) floor(bsize / point_size);
  630.         p->landscape_cols = (int) floor(bsize * p->charsperinch / 72.0);
  631.     }
  632.  
  633.     if (lflag) {
  634.         start_y = LANDSCAPE_START_Y;
  635.         start_x = LANDSCAPE_START_X + margin + (int) (offset * 72.0);
  636.         lines_per_page = p->landscape_page_length;
  637.         columns = p->landscape_cols - (int) ceil(offset * p->charsperinch);
  638.         if (mflag) {
  639.             int nlines;
  640.  
  641.             nlines = (int) ceil((THREE_HOLE_Y * 72.0) / point_size);
  642.             start_y -= (nlines * point_size);
  643.             lines_per_page -= nlines;
  644.             columns -= (int) ceil(THREE_HOLE_Y * p->charsperinch);
  645.         }
  646.     }
  647.     else {
  648.         start_y = PORTRAIT_START_Y - margin;
  649.         lines_per_page = p->portrait_page_length;
  650.         start_x = PORTRAIT_START_X;
  651.         if (mflag)
  652.                 offset += THREE_HOLE_X;
  653.         start_x += (int) (offset * 72.0);
  654.         columns = p->portrait_cols - (int) ceil(offset * p->charsperinch);
  655.     }
  656.     if (vflag) {
  657.         (void) fprintf(stderr, "%s\n\n", version);
  658.         (void) fprintf(stderr, "Lines/page = %d\n", lines_per_page);
  659.         (void) fprintf(stderr, "Columns = %d\n", columns);
  660.         (void) fprintf(stderr, "X-offset = %5.2f inches\n", offset);
  661.     }
  662.  
  663.     setup();
  664.     preamble();
  665.  
  666.     first_file = i;
  667.  
  668.     if (!rflag && Sflag)
  669.         header(argc - first_file, argv + first_file);
  670.  
  671.     if (i == argc) {    /* no files on command line */
  672.         infile = stdin;
  673. #ifdef PR
  674.         if (pflag) {
  675.             build_prcmd(bufin, "");
  676.             if ((infile = popen(bufin, "r")) == (FILE *) NULL) {
  677.                 fatal("popen failed");
  678.                 /*NOTREACHED*/
  679.             }
  680.         }
  681. #endif PR
  682.         if (vflag)
  683.             (void) fprintf(stderr, "printing stdin\n");
  684.         print(infile);
  685. #ifdef PR
  686.         if (pflag)
  687.             (void) pclose(infile);
  688. #endif PR
  689.     }
  690.  
  691.     /*
  692.      * If page reversal is performed, process the file arguments right to left,
  693.      * oth. left to right
  694.      * If the correct flag is used for the printer the first file argument
  695.      * will be on top in the printer's output tray when the paper is removed
  696.      */
  697.     if (rflag)
  698.         j = argc - 1;
  699.     else
  700.         j = i;
  701.     while (i < argc) {
  702.         infile = stdin;
  703. #ifdef PR
  704.         if (pflag) {
  705.             build_prcmd(bufin, argv[j]);
  706.             if ((infile = popen(bufin, "r")) == (FILE *) NULL) {
  707.                 fatal("popen failed");
  708.                 /*NOTREACHED*/
  709.             }
  710.         }
  711.         else {
  712. #endif PR
  713.             if (freopen(argv[j], "r", stdin) == (FILE *) NULL) {
  714.                 fatal("can't open %s", argv[j]);
  715.                 /*NOTREACHED*/
  716.             }
  717. #ifdef PR
  718.         }
  719. #endif PR
  720.         if (vflag)
  721.             (void) fprintf(stderr, "printing %s\n", argv[j]);
  722.         print(infile);
  723. #ifdef PR
  724.         if (pflag)
  725.             (void) pclose(infile);
  726. #endif PR
  727.         if (rflag)
  728.             j--;
  729.         else
  730.             j++;
  731.         i++;
  732.     }
  733.  
  734.     if (rflag && Sflag)
  735.         header(argc - first_file, argv + first_file);
  736.  
  737.     (void) printf("%%%%Trailer\n");
  738.     (void) printf("%%%%Pages: %d\n", page_count);
  739.     (void) putc(PS_EOF, stdout);
  740.  
  741.     if (fflush(stdout) == EOF) {
  742.         fatal("write error on stdout");
  743.         /*NOTREACHED*/
  744.     }
  745.     exit(0);
  746. }
  747.  
  748. /* initialize variables */
  749. init()
  750. {
  751.     dflag = 0;
  752.     lflag = 0;
  753.     mflag = 0;
  754.     nflag = 0;
  755.     vflag = 0;
  756.     wflag = 0;
  757.     Sflag = 0;
  758.     rflag = REVERSE;
  759. #ifdef PR
  760.     pflag = 0;
  761.     propts = "";
  762. #endif PR
  763. #ifdef PROLOGUE
  764.     prologue = PROLOGUE;
  765. #else PROLOGUE
  766.     prologue = "";
  767. #endif PROLOGUE
  768.     ncopies = 1;
  769.     offset = 0.0;
  770.     range = ":";
  771.     tabstop = DEFAULT_TAB_SIZE;
  772.     p = get_psize(DEFAULT_POINT_SIZE);
  773.     page_count = 0;
  774.     margin = 0;
  775. }
  776.  
  777. /* read the configuration file for defaults */
  778. getdefaults()
  779. {
  780.     FILE *deffile;
  781.     char inline[100];
  782.     char *pp;
  783.     int j;
  784.  
  785.     if ((deffile = fopen(configfile, "r")) == NULL)
  786.         return;
  787.  
  788.     while (fgets(inline, 100, deffile) != NULL) {
  789.         switch (inline[0]) {
  790.         case ' ': case '*': case '#': case ';': case '\n':
  791.                   break;
  792.         case 'c':
  793.                   (void) sscanf(inline+1, "%d", &ncopies);
  794.                   break;
  795.         case 'd':
  796.                   dflag = 1;
  797.                  break ;
  798.         case 'i':
  799.                   (void) sscanf(inline+1, "%lf", &offset);
  800.                   break;
  801.         case 'l':
  802.                          lflag = 1;
  803.                  break ;
  804.         case 'm':
  805.                          mflag = 1;
  806.                  break ;
  807.         case 'n':
  808.                          nflag = 1;
  809.                  break ;
  810.         case 'o':
  811.                          (void) sscanf(inline+1, "%s", pp);
  812.                          range = pp;
  813.                          while (*pp++);
  814.                          break;
  815. #ifdef PR
  816.         case 'p':
  817.                  pflag = 1;
  818.                  (void) sscanf(inline+1, "%s", pp);
  819.                          propts = pp;
  820.                          while (*pp++);
  821.                  break ;
  822. #endif PR
  823.         case 'P':
  824.                  (void) sscanf(inline+1, "%s", pp);
  825.                          prologue = pp;
  826.                          while (*pp++);
  827.                  break ;
  828.         case 'r':
  829.                  rflag = !rflag;
  830.                  break;
  831.         case 's':
  832.                          (void) sscanf(inline+1, "%d", &j);
  833.                          if ((p = get_psize(j)) == (struct psize *) NULL) {
  834.                              fatal("bad point size");
  835.                              /*NOTREACHED*/
  836.                          }
  837.                          break;
  838.         case 't':
  839.                  (void) sscanf(inline+1, "%d", &tabstop);
  840.                  break;
  841.         case 'v':
  842.                          vflag = 1;
  843.                  break ;
  844.         case 'w':
  845.                          wflag = 1;
  846.                  break ;
  847.         case 'S':
  848.                          Sflag = 1;
  849.                  break ;
  850.         default:
  851.                  printf("Error in %s file: %s", configfile, inline) ;
  852.         }
  853.     }
  854. }
  855.  
  856. /*
  857.  * Return a pointer to the point size structure for the
  858.  * specified point size
  859.  */
  860. struct psize *
  861. get_psize(size)
  862. int size;
  863. {
  864.     register int i;
  865.  
  866.     for (i = 0; i < NPSIZES; i++)
  867.         if (psize[i].size == size)
  868.             break;
  869.     if (i == NPSIZES)
  870.         return((struct psize *) NULL);
  871.     return(&psize[i]);
  872. }
  873.  
  874. /*
  875.  * Initial lines sent to the LaserWriter.
  876.  * This stuff is sent to stdout since we don't want it to be reversed.
  877.  * Generates the PostScript header and includes the prologue file.
  878.  * There is limited checking for I/O errors here.
  879.  * When the standard prologue is being used we probably should verify
  880.  * that it is the correct version (via %%BeginProcSet).
  881.  */
  882. preamble()
  883. {
  884.     FILE *fp;
  885.  
  886.     (void) printf("%%!PS-Adobe-2.0\n");
  887.     (void) printf("%%%%Creator: %s on %s\n", progname, hostname);
  888.     (void) printf("%%%%CreationDate: %s\n", currentdate);
  889.     (void) printf("%%%%For: %s\n", username);
  890.     (void) printf("%%%%DocumentFonts: %s", TEXTFONT);
  891.     if (Sflag)
  892.         (void) printf(" %s\n", HEADERFONT);
  893.     else
  894.         (void) printf("\n");
  895.     (void) printf("%%%%Pages: (atend)\n");
  896.  
  897.     if (strcmp(prologue, "")) {
  898.         if ((fp = fopen(prologue, "r")) == (FILE *) NULL) {
  899.             fatal("can't open prologue file `%s'", prologue);
  900.             /*NOTREACHED*/
  901.         }
  902.  
  903.         while (fgets(bufin, sizeof(bufin), fp) != (char *) NULL)
  904.             (void) fputs(bufin, stdout);
  905.         (void) fclose(fp);
  906.     } else {
  907.         (void) printf("%%%%EndComments\n");
  908.         (void) printf("%% PostScript Prologue for lwf V2.0a\n");
  909.         (void) printf("/B {NW 0 rmoveto}bind def\n");
  910.         (void) printf("/EP {SV restore /#copies exch def showpage}bind def\n");
  911.         (void) printf("/L /lineto load def\n");
  912.         (void) printf("/M /moveto load def\n");
  913.         (void) printf("/NP /newpath load def\n");
  914.         (void) printf("/S /show load def\n");
  915.         (void) printf("/SHP {SP 2 setlinewidth}bind def\n");
  916.         (void) printf("/SLP {SP 612 0 translate 90 rotate}bind def\n");
  917.         (void) printf("/SP {/SV save def findfont exch scalefont setfont ( )\n");
  918.         (void) printf("  stringwidth pop dup /W exch def neg /NW exch def}bind def\n");
  919.         (void) printf("/ST /stroke load def\n");
  920.         (void) printf("/T {W mul 0 rmoveto}bind def\n");
  921.         (void) printf("%%%%EndProlog\n");
  922.     }
  923.     if (ferror(stdout) || fflush(stdout) == EOF) {
  924.         fatal("write error on stdout");
  925.         /*NOTREACHED*/
  926.     }
  927. }
  928.  
  929. #ifdef PR
  930. /*
  931.  * Generate a command, in the specified buffer, to print the given file
  932.  * according to the options in effect.
  933.  */
  934. build_prcmd(buf, file)
  935. char *buf, *file;
  936. {
  937.  
  938. #ifdef SYSV
  939.     (void) sprintf(buf, "%s -e%d -w%d -l%d -s%c %s %s",
  940.                 PR, tabstop, columns, lines_per_page, SEP_CHAR, propts, file);
  941. #else SYSV
  942.     (void) sprintf(buf, "%s -w%d -l%d -s%c %s %s",
  943.                         PR, columns, lines_per_page, SEP_CHAR, propts, file);
  944. #endif SYSV
  945.     if (vflag)
  946.         (void) fprintf(stderr, "pr cmd: %s\n", buf);
  947. }
  948. #endif PR
  949.  
  950. /*
  951.  * Print a file
  952.  *
  953.  * The input stream may be stdin, a file, or a pipe.
  954.  * If page reversal is being performed, the output goes to a temporary file and
  955.  * then reverse() is called to do the page reversal to stdout.
  956.  */
  957. print(infile)
  958. FILE *infile;
  959. {
  960.     register int eof, pagenum, r;
  961.     register char *p;
  962.     FILE *outfile;
  963.     char *mktemp();
  964.  
  965.     if (rflag) {
  966.         static char bigbuf[BUFSIZ];
  967.  
  968.         page = 0;
  969.         page_map[page] = 0L;
  970. #ifdef AMIGA
  971.         (void) sprintf(bufin, "ram:%sXXXXXX", progname);
  972. #else AMIGA
  973.         (void) sprintf(bufin, "/tmp/%sXXXXXX", progname);
  974. #endif AMIGA
  975.         if (vflag)
  976.             (void) fprintf(stderr, "temp will be: %s ... ", bufin);
  977.         p = mktemp(bufin);
  978.         if (vflag)
  979.             (void) fprintf(stderr, "%s\n", p);
  980.         if ((outfile = fopen(p, "w+")) == (FILE *) NULL) {
  981.             (void) fprintf(stderr, "%s: can't create %s\n", progname, p);
  982.             cleanup();
  983.             /*NOTREACHED*/
  984.         }
  985.         setbuf(outfile, bigbuf);
  986. #ifdef AMIGA
  987.         (void) strcpy(tmpname, p);
  988. #else AMIGA
  989.         if (!dflag)
  990.             (void) unlink(p);
  991.         else
  992.             (void) fprintf(stderr, "will not unlink %s\n", p);
  993. #endif AMIGA
  994.     }
  995.     else
  996.         outfile = stdout;
  997.  
  998.     pagenum = 1;
  999.     eof = 0;
  1000.     while (!eof) {
  1001.         row = start_y;
  1002.         if ((r = inrange(pagenum, range)) == -1) {
  1003.             cleanup();
  1004.             /*NOTREACHED*/
  1005.         }
  1006.         else if (r == 1)
  1007.             eof = printpage(infile, outfile);
  1008.         else if (r == 0)
  1009.             eof = flushpage(infile);
  1010.         else {
  1011.             fatal("bad inrange result");
  1012.             /*NOTREACHED*/
  1013.         }
  1014.         pagenum++;
  1015.     }
  1016.     if (row != start_y)
  1017.         endpage(outfile);
  1018.     if (vflag)
  1019.         (void) fprintf(stderr, "\n");
  1020.     if (fflush(outfile) == EOF) {
  1021.         fatal("write error while flushing output");
  1022.         /*NOTREACHED*/
  1023.     }
  1024.     if (rflag) {
  1025.         reverse(outfile);
  1026.         (void) fclose(outfile);
  1027. #ifdef AMIGA
  1028.         (void) unlink(tmpname);
  1029. #endif AMIGA
  1030.     }
  1031. }
  1032.  
  1033. /*
  1034.  * Process the next page.
  1035.  * Return 1 on EOF, 0 otherwise.
  1036.  */
  1037. printpage(infile, outfile)
  1038. FILE *infile, *outfile;
  1039. {
  1040.     register int lineno;
  1041.  
  1042.     if (ungetc(getc(infile), infile) == EOF)
  1043.         return(1);
  1044.  
  1045.     startpage(page_count + 1, outfile);
  1046.     for (lineno = 0; lineno < lines_per_page; lineno++) {
  1047.         if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL)
  1048.             return(1);
  1049.         if (bufin[0] == '\f')
  1050.             break;
  1051.         if (bufin[0] != '\0') {
  1052.             (void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
  1053.             proc(bufin, outfile);
  1054.         }
  1055.         row -= point_size;
  1056.     }
  1057.     endpage(outfile);
  1058.     return(0);
  1059. }
  1060.  
  1061. /*
  1062.  * The next page will not be printed; just consume the input and discard.
  1063.  * Don't change xrow since we don't want an endpage().
  1064.  */
  1065. flushpage(infile)
  1066. FILE *infile;
  1067. {
  1068.     register int lineno, xrow;
  1069.  
  1070.     xrow = row;
  1071.     for (lineno = 0; lineno < lines_per_page; lineno++) {
  1072.         if (fgetline(bufin, sizeof(bufin), infile) == (char *) NULL)
  1073.             return(1);
  1074.         if (bufin[0] == '\f')
  1075.             break;
  1076.         xrow -= point_size;
  1077.     }
  1078.     return(0);
  1079. }
  1080.  
  1081. /*
  1082.  * Start a new page.
  1083.  */
  1084. startpage(n, outfile)
  1085. int n;
  1086. FILE *outfile;
  1087. {
  1088.  
  1089.     (void) fprintf(outfile, "%%%%Page: %d %d\n", n, n);
  1090.     (void) fprintf(outfile, "%d /%s %s\n",
  1091.             point_size, TEXTFONT, lflag ? STARTLPAGE : STARTPAGE);
  1092. }
  1093.  
  1094. /*
  1095.  * A page has been written to the temp file.
  1096.  * Record the start of the next page.
  1097.  * Terminate the page and indicate the start of the next.
  1098.  */
  1099. endpage(outfile)
  1100. FILE *outfile;
  1101. {
  1102.     long ftell();
  1103.  
  1104.     if (page_count == MAXPAGES) {
  1105.         fatal("pagelimit (%d) reached", MAXPAGES);
  1106.         /*NOTREACHED*/
  1107.     }
  1108.     (void) fprintf(outfile, "%d %s\n", ncopies, ENDPAGE);
  1109.     if (rflag) {
  1110.         if (fflush(outfile) == EOF) {
  1111.             fatal("write error while flushing page");
  1112.             /*NOTREACHED*/
  1113.         }
  1114.         page_map[++page] = ftell(outfile);
  1115.     }
  1116.  
  1117.     page_count++;
  1118.  
  1119.     if (vflag)
  1120.         (void) fprintf(stderr, "x");
  1121. }
  1122.  
  1123. /*
  1124.  * Print the pages to stdout in reverse order.
  1125.  * Assumes that the number of characters per page can be contained in an int.
  1126.  */
  1127. reverse(outfile)
  1128. FILE *outfile;
  1129. {
  1130.     register int i;
  1131.     int bytecount, nbytes;
  1132.     int fseek();
  1133.  
  1134.     if (vflag)
  1135.         (void) fprintf(stderr, "\nreversing %d page%s\n", page,
  1136.                         page > 1 ? "s" : "");
  1137.     if (dflag) {
  1138.         for (i = 0; i <= page; i++)
  1139.             (void) fprintf(stderr, "[%ld]\n", page_map[i]);
  1140.     }
  1141.     for (i = page - 1; i >= 0; i--) {
  1142.         if (fseek(outfile, page_map[i], 0) == -1L) {
  1143.             fatal("seek error");
  1144.             /*NOTREACHED*/
  1145.         }
  1146.         nbytes = (int) (page_map[i + 1] - page_map[i]);
  1147.         while (nbytes > 0) {
  1148.             bytecount = min(nbytes, sizeof(bufout));
  1149.             if (fread(bufout, 1, bytecount, outfile) != bytecount) {
  1150.                 fatal("read error while reversing pages");
  1151.                 /*NOTREACHED*/
  1152.             }
  1153.             if (fwrite(bufout, 1, bytecount, stdout) != bytecount) {
  1154.                 fatal("write error while reversing pages");
  1155.                 /*NOTREACHED*/
  1156.             }
  1157.             nbytes -= bytecount;
  1158.         }
  1159.     }
  1160. }
  1161.  
  1162. /*
  1163.  * Process a line of input, escaping characters when necessary and handling
  1164.  * tabs.
  1165.  *
  1166.  * The output is improved somewhat by coalescing consecutive tabs and
  1167.  * backspaces and eliminating tabs at the end of a line.
  1168.  *
  1169.  * Overprinting (presumably most often used in underlining) can be far from
  1170.  * optimal; in particular the way nroff underlines by sequences like
  1171.  * "_\ba_\bb_\bc" creates a large volume of PostScript.  This isn't too
  1172.  * serious since a lot of nroff underlining is unlikely.
  1173.  *
  1174.  * Since a newline is generated for each call there will be more
  1175.  * newlines in the output than is necessary.
  1176.  */
  1177. proc(in, outfile)
  1178. char *in;
  1179. FILE *outfile;
  1180. {
  1181.     register int i;
  1182.     register char *last, *p, *q;
  1183.     int currentp, instr, tabc, tabto;
  1184.     char *savep;
  1185.     static int colskip, ncols;
  1186.     static int seen_sep = 0;
  1187.  
  1188.     currentp = 0;
  1189.     instr = 0;
  1190.     tabto = 0;
  1191.     last = bufout + MAX_OUTPUT_LINE_LENGTH - 20;    /* subtract slop factor */
  1192.  
  1193.     q = bufout;
  1194.     *q = '\0';
  1195.     for (p = in; *p != '\0'; p++) {
  1196.         switch (*p) {
  1197.         case SEP_CHAR:
  1198.             /*
  1199.              * This assumes that the input buffer contains the entire line
  1200.              * otherwise the column count will be off.
  1201.              * Also, the input stream must be formatted into a constant number
  1202.              * of columns oth. it would be necessary to scan each line to
  1203.              * count SEP_CHARs (which is not hard but could be slow).
  1204.              */
  1205.             if (!seen_sep) {    /* discern number of columns */
  1206.                 seen_sep = 1;
  1207.                 ncols = 2;    /* there are at least two columns... */
  1208.                 savep = p++;
  1209.                 while (*p != '\0') {
  1210.                     if (*p++ == SEP_CHAR)
  1211.                         ncols++;
  1212.                 }
  1213.                 p = savep;
  1214.                 colskip = columns / ncols;
  1215.                 if (vflag)
  1216.                     (void) fprintf(stderr, "Using %d columns\n", ncols);
  1217.             }
  1218.             if (instr) {
  1219.                 (void) sprintf(q, ")%s ", SHOW);
  1220.                 q += strlen(q);
  1221.                 instr = 0;
  1222.             }
  1223.             tabto += (colskip - currentp);
  1224.             currentp = 0;
  1225.             break;
  1226.         case '\t':
  1227.             /*
  1228.              * Count the number of tabs that immediately follow the one we're
  1229.              * looking at
  1230.              */
  1231.             tabc = 0;
  1232.             while (*(p + 1) == '\t') {
  1233.                 p++;
  1234.                 tabc++;
  1235.             }
  1236.             if (currentp > 0) {        /* not beginning of line */
  1237.                 i = tabstop - (currentp % tabstop) + tabc * tabstop;
  1238.                 if (instr) {
  1239.                     (void) sprintf(q, ")%s ", SHOW);
  1240.                     q += strlen(q);
  1241.                     instr = 0;
  1242.                 }
  1243.             }
  1244.             else
  1245.                 i = (tabc + 1) * tabstop;
  1246.             tabto += i;
  1247.             currentp += i;
  1248.             break;
  1249.         case '\b':
  1250.             *q = '\0';
  1251.             (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  1252.             /* backspacing over tabs doesn't work... */
  1253.             if (tabto != 0) {
  1254.                 fatal("attempt to backspace over a tab");
  1255.                 /*NOTREACHED*/
  1256.             }
  1257.             p++;
  1258.             for (i = 1; *p == '\b'; p++)
  1259.                 i++;
  1260.             if (currentp - i < 0) {
  1261.                 fatal("too many backspaces");
  1262.                 /*NOTREACHED*/
  1263.             }
  1264.             if (!instr) {
  1265.                 fatal("bad backspacing");
  1266.                 /*NOTREACHED*/
  1267.             }
  1268.             if (i == 1)                /* frequent case gets special attention */
  1269.                 (void) sprintf(bufout, "%s (", BACKSPACE);
  1270.             else
  1271.                 (void) sprintf(bufout, "-%d %s (", i, TAB);
  1272.             currentp -= i;
  1273.             q = bufout + strlen(bufout);
  1274.             p--;
  1275.             break;
  1276.         case '\f':
  1277.             tabto = 0;                            /* optimizes */
  1278.             *q = '\0';
  1279.             if (instr)
  1280.                 (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  1281.             else
  1282.                 (void) fprintf(outfile, "%s\n", bufout);
  1283.             endpage(outfile);
  1284.             startpage(page_count + 1, outfile);
  1285.             row = start_y;
  1286.             (void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
  1287.             q = bufout;
  1288.             currentp = 0;
  1289.             instr = 0;
  1290.             break;
  1291.         case '\r':
  1292.             tabto = 0;                            /* optimizes */
  1293.             if (instr) {
  1294.                 *q = '\0';
  1295.                 (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  1296.                 instr = 0;
  1297.                 q = bufout;
  1298.             }
  1299.             (void) fprintf(outfile, "%d %d %s\n", start_x, row, MOVETO);
  1300.             currentp = 0;
  1301.             break;
  1302.         case '\\':
  1303.         case '(':
  1304.         case ')':
  1305.             if (!instr) {
  1306.                 if (tabto) {
  1307.                     (void) sprintf(q, "%d %s ", tabto, TAB);
  1308.                     q += strlen(q);
  1309.                     tabto = 0;
  1310.                 }
  1311.                 *q++ = '(';
  1312.                 instr = 1;
  1313.             }
  1314.             *q++ = '\\';
  1315.             *q++ = *p;
  1316.             currentp++;
  1317.             break;
  1318.         default:
  1319.             /*
  1320.              * According to the PostScript Language Manual, PostScript files
  1321.              * can contain only "the printable subset of the ASCII character
  1322.              * set (plus the newline marker)".
  1323.              */
  1324.             if (!isascii(*p) || !isprint(*p)) {
  1325.                 fatal("bad character in input");
  1326.                 /*NOTREACHED*/
  1327.             }
  1328.             if (!instr) {
  1329.                 if (tabto) {
  1330.                     (void) sprintf(q, "%d %s ", tabto, TAB);
  1331.                     q += strlen(q);
  1332.                     tabto = 0;
  1333.                 }
  1334.                 *q++ = '(';
  1335.                 instr = 1;
  1336.             }
  1337.             *q++ = *p;
  1338.             currentp++;
  1339.             break;
  1340.         }
  1341.         if (q >= last) {
  1342.             *q = '\0';
  1343.             if (instr)
  1344.                 (void) fprintf(outfile, "%s)%s\n", bufout, SHOW);
  1345.             else
  1346.                 (void) fprintf(outfile, "%s\n", bufout);
  1347.             q = bufout;
  1348.             instr = 0;
  1349.         }
  1350.     }
  1351.     if (instr) {
  1352.         (void) sprintf(q, ")%s", SHOW);
  1353.         q += strlen(q);
  1354.     }
  1355.     else
  1356.         *q = '\0';
  1357.     if (q >= last) {
  1358.         fatal("bufout overflow");
  1359.         /*NOTREACHED*/
  1360.     }
  1361.     if (bufout[0] != '\0')
  1362.         (void) fprintf(outfile, "%s\n", bufout);
  1363. }
  1364.  
  1365. /*
  1366.  * Find out who the user is, etc.
  1367.  * Possible system dependencies here...
  1368.  */
  1369. setup()
  1370. {
  1371.     int len;
  1372.     char *p;
  1373.     long t, time();
  1374.     int getusername(), gethostname();
  1375.     char *ctime(), *getlogin(), *malloc();
  1376.     struct passwd *pw, *getpwuid();
  1377.  
  1378. #ifdef AMIGA
  1379.     (void) getusername(username, sizeof(username));
  1380. #else AMIGA
  1381.     if ((p = getlogin()) == (char *) NULL) {
  1382.         if ((pw = getpwuid(getuid())) == (struct passwd *) NULL)
  1383.             p = "Whoknows";
  1384.         else
  1385.             p = pw->pw_name;
  1386.         endpwent();
  1387.     }
  1388.     username = (char *) malloc((unsigned) (strlen(p) + 1));
  1389.     (void) strcpy(username, p);
  1390. #endif AMIGA
  1391.  
  1392. #ifdef HOSTNAME
  1393.     (void) strncpy(hostname, HOSTNAME, sizeof(hostname));
  1394.     hostname[sizeof(hostname) - 1] = '\0';
  1395. #else HOSTNAME
  1396.     (void) gethostname(hostname, sizeof(hostname));
  1397. #endif HOSTNAME
  1398.  
  1399.     t = time((long *) 0);
  1400.     p = ctime(&t);
  1401.     len = strlen(p);
  1402.     *(p + len - 1) = '\0';        /* zap the newline character */
  1403.     currentdate = (char *) malloc((unsigned) len);
  1404.     (void) strcpy(currentdate, p);
  1405. }
  1406.  
  1407. /*
  1408.  * Print a header page.
  1409.  * Assumes setup() has already been called to fill in the user, host, etc.
  1410.  * Uses HEADERFONT in HEADERPS point.
  1411.  */
  1412. header(nfiles, files)
  1413. int nfiles;
  1414. char **files;
  1415. {
  1416.     register int i;
  1417.     register char *p;
  1418.  
  1419.     if (vflag) {
  1420.         (void) fprintf(stderr, "printing header\n");
  1421.         (void) fprintf(stderr, "%d file%s are:\n", nfiles,
  1422.                             nfiles > 1 ? "s" : "");
  1423.         if (nfiles == 0)
  1424.             (void) fprintf(stderr, "\tstdin\n");
  1425.         for (i = 0; i < nfiles; i++)
  1426.             (void) fprintf(stderr, "\t%s\n", files[i]);
  1427.     }
  1428.  
  1429.     ++page_count;
  1430.  
  1431.     (void) fprintf(stdout, "\n%%%%Page: %d %d\n", page_count, page_count);
  1432.     (void) fprintf(stdout, "%d /%s %s\n", HEADERPS, HEADERFONT, STARTHPAGE);
  1433.  
  1434.     /*
  1435.      * The header sheet looks like:
  1436.      *
  1437.      * ----------------------------
  1438.      * ----------------------------
  1439.      *
  1440.      * User:
  1441.      * Host:
  1442.      * Date:
  1443.      * Files:
  1444.      *
  1445.      * ----------------------------
  1446.      * ----------------------------
  1447.      */
  1448.     row = START_Y_HEADER;
  1449.     (void) printf("%s %d %d %s\n", NEWPATH, START_X_HEADER, row, MOVETO);
  1450.     (void) printf("%d %d %s\n", START_X_HEADER + 400, row, LINETO);
  1451.     row -= 6;
  1452.     (void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
  1453.     (void) printf("%d %d %s\n", START_X_HEADER + 400, row, LINETO);
  1454.     row -= 24;
  1455.     (void) printf("%s\n", STROKE);
  1456.  
  1457.     (void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
  1458.     (void) sprintf(bufin, "User: %s", username);
  1459.     proc(bufin, stdout);
  1460.     row -= 24;
  1461.     (void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
  1462.     (void) sprintf(bufin, "Host: %s", hostname);
  1463.     proc(bufin, stdout);
  1464.     row -= 24;
  1465.     (void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
  1466.     (void) sprintf(bufin, "Date: %s", currentdate);
  1467.     proc(bufin, stdout);
  1468.     row -= 24;
  1469.  
  1470.     if (nfiles == 0) {
  1471.         (void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
  1472.         (void) sprintf(bufin, "File: <stdin>");
  1473.         proc(bufin, stdout);
  1474.     }
  1475.     else {
  1476.         register int len, max, sum;
  1477.  
  1478.         /*
  1479.          * If the list of files is "too long" we'll only print as many as
  1480.          * possible
  1481.          * Arbitrary chop off point is 50 characters
  1482.          * (assume bufin is bigger than this)
  1483.          */
  1484.         (void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
  1485.         (void) sprintf(bufin, "File%s: ", nfiles > 1 ? "s" : "");
  1486.         p = bufin + (sum = strlen(bufin));
  1487.         max = 50;
  1488.         for (i = 0; i < nfiles - 1; i++) {
  1489.             sum += (len = strlen(files[i]) + 1);
  1490.             if (sum >= max)
  1491.                 break;
  1492.             (void) sprintf(p, "%s,", files[i]);
  1493.             p += len;
  1494.         }
  1495.         sum += (len = strlen(files[i]) + 1);
  1496.         if (sum < max)
  1497.             (void) sprintf(p, "%s", files[i]);
  1498.         else
  1499.             (void) strcpy(p, "...");
  1500.         proc(bufin, stdout);
  1501.     }
  1502.  
  1503.     row -= 12;
  1504.     (void) printf("%s %d %d %s\n", NEWPATH, START_X_HEADER, row, MOVETO);
  1505.     (void) printf("%d %d %s\n", START_X_HEADER + 400, row, LINETO);
  1506.     row -= 6;
  1507.     (void) printf("%d %d %s\n", START_X_HEADER, row, MOVETO);
  1508.     (void) printf("%d %d %s\n", START_X_HEADER + 400, row, LINETO);
  1509.     (void) printf("%s\n", STROKE);
  1510.     (void) printf("1 %s\n", ENDPAGE);
  1511.     if (fflush(stdout) == EOF) {
  1512.         fatal("write error on stdout");
  1513.         /*NOTREACHED*/
  1514.     }
  1515. }
  1516.  
  1517. /*
  1518.  * Special version of fgets.
  1519.  * Read until a formfeed, newline, or overflow.
  1520.  * If a formfeed is the first character, return it immediately.
  1521.  * If a formfeed is found after the first character, replace it by a newline
  1522.  * and push the formfeed back onto the input stream.
  1523.  * A special case is a formfeed followed by a newline in which case the
  1524.  * newline is ignored .
  1525.  * The input buffer will be null-terminated and will *not* end with a newline.
  1526.  * The buffer size n includes the null.
  1527.  */
  1528. char *
  1529. fgetline(s, n, iop)
  1530. char *s;
  1531. int n;
  1532. register FILE *iop;
  1533. {
  1534.     register int ch;
  1535.     register char *cs;
  1536.  
  1537.     if (n < 2) {
  1538.         fatal("fgetline called with bad buffer size!?");
  1539.         /*NOTREACHED*/
  1540.     }
  1541.  
  1542.     cs = s;
  1543.     n--;                                /* the null */
  1544.  
  1545.     /*
  1546.      * Check out the special cases
  1547.      */
  1548.     if ((ch = getc(iop)) == EOF)
  1549.         return((char *) NULL);
  1550.     if (ch == '\f') {
  1551. #ifdef PR
  1552. #ifdef PRBUG
  1553.         if (pflag) {
  1554.             /*
  1555.              * Filter out the formfeeds
  1556.              */
  1557.             do {
  1558.                 if (ch == '\f')
  1559.                     continue;
  1560.                 if (ch == '\n')
  1561.                     break;
  1562.                 *cs++ = ch;
  1563.                 n--;
  1564.             } while (n > 0 && (ch = getc(iop)) != EOF);
  1565.             if (ch == EOF) {
  1566.                 if (ungetc(ch, iop) == EOF && !feof(iop)) {
  1567.                     /* Shouldn't happen since a getc() was just done */
  1568.                     fatal("fgetline - ungetc failed");
  1569.                     /*NOTREACHED*/
  1570.                 }
  1571.             }
  1572.             else if (ch != '\n') {
  1573.                 fatal("fgetline - input line too long");
  1574.                 /*NOTREACHED*/
  1575.             }
  1576.             *cs = '\0';
  1577.             return(s);
  1578.         }
  1579. #endif PRBUG
  1580. #endif PR
  1581.         if ((ch = getc(iop)) != '\n') {
  1582.             /*
  1583.              * If EOF was just read it will be noticed next time through
  1584.              */
  1585.             if (ungetc(ch, iop) == EOF && !feof(iop)) {
  1586.                 /* Shouldn't happen since a getc() was just done */
  1587.                 fatal("fgetline - ungetc failed");
  1588.                 /*NOTREACHED*/
  1589.             }
  1590.         }
  1591.         *cs++ = '\f';
  1592.         *cs = '\0';
  1593.         return(s);
  1594.     }
  1595.  
  1596.     /*
  1597.      * Check for "weird" input characters is made in proc()
  1598.      */
  1599.     while (n-- > 0) {
  1600.         if (ch == '\f' || ch == '\n')
  1601.             break;
  1602.         *cs++ = ch;
  1603.         if ((ch = getc(iop)) == EOF)
  1604.             break;
  1605.     }
  1606.  
  1607.     if (ch == EOF && cs == s)        /* Nothing was read */
  1608.         return((char *) NULL);
  1609.     if (ch == '\f') {
  1610.         if (ungetc(ch, iop) == EOF)
  1611.             (void) fprintf(stderr, "fgetline - can't ungetc??\n");
  1612.     }
  1613.     else if (ch != '\n' && ch != EOF) {
  1614.         fatal("fgetline - input line too long");
  1615.         /*NOTREACHED*/
  1616.     }
  1617.     *cs = '\0';
  1618.     return(s);
  1619. }
  1620.  
  1621. /*VARARGS*/
  1622. fatal(s, a, b, c, d, e, f, g, h, i, j)
  1623. char *s;
  1624. {
  1625.  
  1626.     (void) fprintf(stderr, "%s: ", progname);
  1627.     (void) fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j);
  1628.     (void) fprintf(stderr, "\n");
  1629.     cleanup();
  1630.     /*NOTREACHED*/
  1631. }
  1632.  
  1633. /*
  1634.  * Clean up and exit after an error
  1635.  */
  1636. cleanup()
  1637. {
  1638.  
  1639.     exit(1);
  1640. }
  1641. SHAR_EOF
  1642. cat << \SHAR_EOF > lwf.cfg
  1643. #
  1644. # lwf configuration file
  1645. #
  1646.  
  1647. m    ; use 3 hole punch margins
  1648. n    ; leave top and bottom margins
  1649. S    ; print header page
  1650. SHAR_EOF
  1651. cat << \SHAR_EOF > lwf.man
  1652. .TH LWF 1-LOCAL "21 February 1988"
  1653. .UC
  1654. .SH NAME
  1655. lwf \- ASCII to PostScript filter
  1656. .SH SYNOPSIS
  1657. .B lwf
  1658. [-c#] [-d] [-i#] [-l] [-m] [-n] [-olist] [-p[str]]
  1659. .br
  1660. .ti +4
  1661. [-P filename] [-r] [-s#] [-S] [-t#] [-v] [file ...]
  1662. .SH DESCRIPTION
  1663. .I Lwf
  1664. takes one or more ASCII text files as input and produces PostScript
  1665. instructions that may be sent (see \fBlpr(1)\fR) to a PostScript printer
  1666. (e.g., an Apple LaserWriter) for printing.
  1667. If no files are given on the command line, the standard input is read.
  1668. The program correctly handles the form feed character and tabs and
  1669. understands backspacing; underscores followed by backspaces
  1670. may be used to underline.
  1671. Courier font is used.
  1672. The output conforms to the Adobe 2.0 file structuring conventions.
  1673. .PP
  1674. Note that flag arguments apply to all of the files in the argument list.
  1675. For example, using ``-s8'' prints each of the files in 8 point type.
  1676. .PP
  1677. Multiple copies of each page can be printed by immediately following the
  1678. .B -c
  1679. flag with the number of copies to make.
  1680. The pages are not collated.
  1681. .PP
  1682. Lines may be indented (shifted to the right) using the \fB-i\fR flag.
  1683. This flag is immediately followed by the distance in inches to shift
  1684. all text from the left edge of the paper instead of the default amount
  1685. (about 1/3 of an inch); the resolution is approximately 1/72 of an inch.
  1686. .PP
  1687. The
  1688. .B -l
  1689. flag indicates that landscape format is to be used instead of the default
  1690. portrait format.
  1691. .PP
  1692. Margins suitable for use with a three hole punch can be obtained using the
  1693. .B -m
  1694. flag.
  1695. This flag may be used with either portrait or landscape mode.
  1696. In portrait mode the
  1697. .B -m
  1698. flag and any indentation specified by a
  1699. .B -i
  1700. flag are additive.
  1701. .PP
  1702. To get top and bottom margins suitable for printing 66 lines per page
  1703. (with the default 10 point size), use the
  1704. .B -n
  1705. flag.  This
  1706. option works good when printing manual pages.
  1707. .PP
  1708. The
  1709. .B -o
  1710. flag is immediately followed by a range specification that indicates
  1711. which pages are to be printed.
  1712. A range specification is a comma-separated list of numbers and ranges.
  1713. A number N selects the Nth page;
  1714. a range N:M selects the Nth through Mth pages, inclusive;
  1715. an initial :N selects from the beginning up to and including the
  1716. Nth page; and a final N: selects from the Nth page to the end.
  1717. The default, ``:'', is to print all pages.
  1718. .PP
  1719. The
  1720. .B -p
  1721. flag indicates that
  1722. .B pr(1)
  1723. is to be used to perform pagination and print page headers.
  1724. An argument string to be passed on to \fBpr(1)\fR can immediately
  1725. follow the flag.
  1726. The usual way of producing multicolumn output is to pass a
  1727. .B -n
  1728. flag to
  1729. \fBpr(1)\fR, where
  1730. .B n
  1731. is the number of columns to generate.
  1732. Note that this string must be properly quoted if it contains whitespace,
  1733. metacharacters, backslashes, etc.
  1734. \fILwf\fR passes on the number of lines and columns to
  1735. \fBpr(1)\fR so that multicolumn output is handled correctly.
  1736. When specifying multicolumns you are responsible for selecting
  1737. an appropriate point size and/or landscape format.
  1738. The formfeed character is handled incorrectly by some versions of
  1739. \fBpr(1)\fR when multiple columns have been specified.
  1740. Instead of starting a new column or page it simply passes formfeeds
  1741. through.
  1742. .B Lwf
  1743. filters out these formfeeds.
  1744. .PP
  1745. .I Lwf
  1746. normally includes a standard internal PostScript prologue in its output.
  1747. Optionally, a compile time option is not to include it in 
  1748. .I lwf.
  1749. In this
  1750. case
  1751. .I lwf
  1752. will look for the file lwf.prologue in a standard place (usually 
  1753. /usr/local/lib/lwf.prologue).  The argument following a
  1754. .B -P
  1755. flag specifies a prologue file to be substituted for the standard
  1756. system file.
  1757. .PP
  1758. The default is to perform page reversal, which is correct for printers
  1759. like the Apple LaserWriter.
  1760. The
  1761. .B -r
  1762. flag disables page reversal so that the pages appear in
  1763. the correct sequence in the output tray of printers like the
  1764. NEC Silentwriter LC-890.
  1765. .PP
  1766. The
  1767. .B -s
  1768. flag, immediately followed by a 7, 8, 9, 10, 11, or 12 causes
  1769. the corresponding point size to be used.
  1770. The default point size is 10.
  1771. .PP
  1772. By default, the output of
  1773. .I lwf
  1774. is intended to go through a spooler that monitors the status of the
  1775. printer and separates jobs.
  1776. The
  1777. .B -S
  1778. flag indicates that such a monitor is not being used and that a
  1779. header page should be printed to separate jobs and end-of-file characters
  1780. should be inserted in the output.
  1781. .PP
  1782. The
  1783. .B -t
  1784. flag is immediately followed by a number indicating the distance between
  1785. tab stops.
  1786. The default value is 8 characters.
  1787. .PP
  1788. The
  1789. .B -d
  1790. and
  1791. .B -v
  1792. flags are used for debugging.
  1793. .SH EXAMPLES
  1794. The command
  1795. .sp 2
  1796. .ti +5
  1797. lwf -p-2 -i0.5 -s12 file1.c file2.c | lpr -Plw
  1798. .sp 2
  1799. would print the two files on printer 'lw' in portrait format with
  1800. page headings, indenting 0.5 inches from the left edge, using 12
  1801. point type.
  1802. Each file would be printed in two columns.
  1803. .sp 1
  1804. The command
  1805. .sp 2
  1806. .ti +5
  1807. lwf -l -s7 -p'-h foo' | lpr -Plw
  1808. .sp 2
  1809. would print the standard input with page headings in landscape format
  1810. using 7 point type.
  1811. The filename for the header line would be 'foo'.
  1812. .sp 1
  1813. The command
  1814. .sp 2
  1815. .ti +5
  1816. lwf -p'-h foo .login -h bar .cshrc' | lpr -Plw
  1817. .sp 2
  1818. would print the file ``.login'' with a header title ``foo'' and ``.cshrc''
  1819. with the header title ``bar''.
  1820. .sp 1
  1821. A useful csh alias is:
  1822. .sp 2
  1823. .ti +5
  1824. alias prlw 'lwf -s7 -t4 -l -p-2 \\!* | lpr -Plw'
  1825. .sp 2
  1826. which, when invoked as
  1827. .sp 2
  1828. .ti +5
  1829. prlw foo1 foo2
  1830. .sp 2
  1831. prints foo1 and foo2, two columns per page, on printer 'lw'.
  1832. .SH FILES
  1833. /tmp/lwfXXXXXX        \- temporary file used for page reversal
  1834. .SH SEE ALSO
  1835. pr(1), lpr(1)
  1836. .SH LIMITATIONS
  1837. The maximum input line length is 1024 characters.
  1838. This should not present a problem since the corresponding output line
  1839. length would be too long to be printed.
  1840. The program quits and prints a message if an input line is too long.
  1841. Output lines that are too long are normally silently truncated by the
  1842. printer.
  1843. .sp 2
  1844. .I Lwf
  1845. can be compiled such that there is a limit on the number of pages it can
  1846. produce.
  1847. .SH BUGS
  1848. It might be argued that flags should be allowed to be interspersed between
  1849. file arguments.
  1850. .sp 2
  1851. The character '\\001' (SOH) should not appear in the input as it is used
  1852. within the program to delimit columns.
  1853. The first time the program reads this character it determines the number
  1854. of columns being printed; all subsequent lines are expected to
  1855. have this number of columns or be ``single column''.
  1856. .sp 2
  1857. A \fB-s\fR flag should not be passed on to \fBpr(1)\fR since
  1858. .I lwf
  1859. uses it.
  1860. .sp 2
  1861. The program can only handle the 6 different point sizes and the single
  1862. font.
  1863. .sp 2
  1864. So much for metric.
  1865. .SH AUTHOR
  1866. Barry Brachman
  1867. .br
  1868. Dept. of Computer Science
  1869. .br
  1870. University of British Columbia
  1871.  
  1872. With additions/revisions and Amiga port by:
  1873.  
  1874. Dr. Samuel Paolucci
  1875. .br
  1876. 1351 Roselli Dr.
  1877. .br
  1878. Livermore, CA 94550
  1879. .br
  1880. (415)294-2018
  1881. .br
  1882. ARPA: paolucci@snll-arpagw.llnl.gov
  1883.  
  1884. SHAR_EOF
  1885. cat << \SHAR_EOF > lwf.prologue
  1886. %%EndComments
  1887. % PostScript Prologue for lwf V2.0a
  1888. /B {NW 0 rmoveto}bind def
  1889. /EP {SV restore /#copies exch def showpage}bind def
  1890. /L /lineto load def
  1891. /M /moveto load def
  1892. /NP /newpath load def
  1893. /S /show load def
  1894. /SHP {SP 2 setlinewidth}bind def
  1895. /SLP {SP 612 0 translate 90 rotate}bind def
  1896. /SP {/SV save def findfont exch scalefont setfont ( )
  1897.   stringwidth pop dup /W exch def neg /NW exch def}bind def
  1898. /ST /stroke load def
  1899. /T {W mul 0 rmoveto}bind def
  1900. %%EndProlog
  1901. SHAR_EOF
  1902. cat << \SHAR_EOF > psclear.uu
  1903.  
  1904. begin 644 psclear
  1905. !!```$
  1906. ``
  1907. end
  1908. size 1
  1909. SHAR_EOF
  1910. cat << \SHAR_EOF > range.c
  1911. /* vi: set tabstop=4 : */
  1912.  
  1913. /*
  1914.  * Return 1 if the given number is in the specified range,
  1915.  * -1 if there is an error in the range specification,
  1916.  * 0 otherwise
  1917.  *
  1918.  * Ranges have a similar (we use a colon instead of a dash) form to that
  1919.  * used by [nt]roff; i.e., a comma separated list of specifiers of the
  1920.  * form:
  1921.  *    1) n    means     x such that x = n
  1922.  *    2) :n    means all x such that x <= n
  1923.  *    3) n:    means all x such that x >= n
  1924.  *    4) n:m    means all x such that n <= x <= m
  1925.  *    5) :    means all x
  1926.  * n is an int
  1927.  *
  1928.  * Problems:
  1929.  * The routine prints an error message if the range is strange - this
  1930.  * might not always be desirable.
  1931.  *
  1932.  * Jul/86 BJB
  1933.  */
  1934.  
  1935. /*
  1936.  * ===================================================================
  1937.  *
  1938.  * Permission is given to freely copy and distribute this software
  1939.  * providing:
  1940.  *
  1941.  *    1) You do not sell it,
  1942.  *    2) You do not use it for commercial advantage, and
  1943.  *    3) This notice accompanies the distribution
  1944.  *
  1945.  * Copyright (c) 1988
  1946.  * Barry Brachman
  1947.  * Dept. of Computer Science
  1948.  * Univ. of British Columbia
  1949.  * Vancouver, B.C. V6T 1W5
  1950.  *
  1951.  * .. {ihnp4!alberta, uw-beaver, uunet}!ubc-vision!ubc-csgrads!brachman
  1952.  * brachman@grads.cs.ubc.cdn
  1953.  * brachman%ubc.csnet@csnet-relay.arpa
  1954.  * brachman@ubc.csnet
  1955.  * ====================================================================
  1956.  */
  1957.  
  1958. #include <ctype.h>
  1959. #include <stdio.h>
  1960.  
  1961. #define streq(a, b)            (!strcmp((a), (b)))
  1962. #define smalloc(a, t, s)    ((a = (t) malloc((unsigned) (s))) == NULL)
  1963. #define srealloc(a, t, b, s) ((a = (t) realloc(b, (unsigned) (s))) == NULL)
  1964. #define max(a, b)            ((a) >= (b) ? (a) : (b))
  1965.  
  1966. #define SEP_CHAR    ':'
  1967. #define SEP_T        0        /* separator token */
  1968. #define NUM_T        1        /* number token */
  1969. #define BAD_T        2        /* token for bad character */
  1970.  
  1971. #define STR_ALLOC    80
  1972.  
  1973. struct range_header {
  1974.     char *range_str;        /* range character string */
  1975.     int range_str_alloc;    /* length in bytes */
  1976.     int nranges;            /* number of range entries */
  1977.     struct range *range;
  1978. } range_header = {
  1979.     NULL, 0, NULL
  1980. };
  1981.  
  1982. /*
  1983.  * If hflag (lflag) is non-zero then the high (low) value is present
  1984.  */
  1985. struct range {
  1986.     char hflag;            /* high value present */
  1987.     char lflag;            /* low value present */
  1988.     int high;            /* high part of range */
  1989.     int low;            /* low part of range */
  1990. };
  1991.  
  1992. #ifdef RANGE_DEBUG
  1993.  
  1994. /*
  1995.  * This is a program for demonstrating and debugging the range checking
  1996.  * code
  1997.  * Enter a range when prompted
  1998.  * (If there is a previous range shown you may enter <return> to
  1999.  * reselect it)
  2000.  * Enter a value
  2001.  * The program will indicate whether the value is in the given range
  2002.  */
  2003.  
  2004. char buf[BUFSIZ], range[BUFSIZ];
  2005.  
  2006. main(argc, argv)
  2007. int argc;
  2008. char **argv;
  2009. {
  2010.     register int i;
  2011.     char *p;
  2012.     struct range_header *rh;
  2013.     struct range *r;
  2014.     FILE *fp;
  2015.     char *gets(), *index(), *strcpy();
  2016.  
  2017.     buf[0] = range[0] = '\0';
  2018.     if (argc == 2) {
  2019.         if ((fp = fopen(argv[1], "r")) == NULL) {
  2020.             (void) fprintf(stderr, "Can't open %s\n", argv[1]);
  2021.             exit(1);
  2022.             /*NOTREACHED*/
  2023.         }
  2024.     }
  2025.     else
  2026.         fp = stdin;
  2027.  
  2028.     if (fp == stdin)
  2029.         (void) printf("Range? ");
  2030.     while (fgets(buf, sizeof(buf), fp) != NULL) {
  2031.         if ((p = index(buf, '\n')) != NULL)
  2032.             *p = '\0';
  2033.         if (buf[0] != '\0') {
  2034.             (void) strcpy(range, buf);
  2035.             if (checkrange(range)) {
  2036.                 if (fp == stdin)
  2037.                     (void) printf("Range? ");
  2038.                 continue;
  2039.             }
  2040.             rh = &range_header;
  2041.             (void) printf("%s (%d alloc) (%d ranges):\n",
  2042.                   rh->range_str, rh->range_str_alloc, rh->nranges);
  2043.             for (r = rh->range, i = 0; i < rh->nranges; i++, r++)
  2044.                 (void) printf("hflag=%d lflag=%d high=%d low=%d\n",
  2045.                     r->hflag, r->lflag, r->high, r->low);
  2046.         }
  2047.         if (fp != stdin)
  2048.             continue;
  2049.         (void) printf("Value? ");
  2050.         if (gets(buf) == NULL)
  2051.             break;
  2052.         i = inrange(atoi(buf), range);
  2053.         if (i == 0)
  2054.             (void) printf("\tno\n");
  2055.         else if (i == 1)
  2056.             (void) printf("\tyes\n");
  2057.         else if (i == -1)
  2058.             (void) printf("\terror\n");
  2059.         else
  2060.             (void) printf("\tbad result\n");
  2061.         (void) printf("Range ['%s']? ", range);
  2062.     }
  2063.     (void) printf("\n");
  2064. }
  2065. #endif RANGE_DEBUG
  2066.  
  2067. /*
  2068.  * Check and compile the given range specification and then determine if
  2069.  * the number is in the range
  2070.  * Return -1 if there is a compilation error, 1 if the number is in the
  2071.  * range, or 0 if the number isn't in the range
  2072.  */
  2073. inrange(num, range_spec)
  2074. int num;
  2075. char *range_spec;
  2076. {
  2077.     register int i, rc;
  2078.     register struct range_header *rh;
  2079.     register struct range *r;
  2080.  
  2081.     if (checkrange(range_spec))
  2082.         return(-1);
  2083.     rh = &range_header;
  2084.     rc = 0;
  2085.     for (r = rh->range, i = 0; rc == 0 && i < rh->nranges; i++, r++) {
  2086.         if (r->hflag) {
  2087.             if (num > r->high)
  2088.                 continue;
  2089.             if (r->lflag && num < r->low)
  2090.                 continue;
  2091.             rc = 1;
  2092.         }
  2093.         else if (r->lflag) {
  2094.             if (num >= r->low)
  2095.                 rc = 1;
  2096.         }
  2097.         else                /* both unset -> ":" */
  2098.             rc = 1;
  2099.     }
  2100.     return(rc);
  2101. }
  2102.  
  2103. /*
  2104.  * Check and compile a range specification
  2105.  * Print a message and return -1 on error; return 0 oth.
  2106.  *
  2107.  * Could be more efficient by allocating more structures at a time... SMOP
  2108.  */
  2109. checkrange(range_spec)
  2110. char *range_spec;
  2111. {
  2112.     register struct range_header *rh;
  2113.     register struct range *r;
  2114.     int len;
  2115.     int ltype, lval, rtype, rval;
  2116.     char *p;
  2117.     char *malloc(), *realloc(), *strcpy();
  2118.  
  2119.     rh = &range_header;
  2120.     /*
  2121.      * Check if the previous range is being used
  2122.      */
  2123.     if (rh->range_str != NULL && streq(range_spec, rh->range_str))
  2124.         return(0);
  2125.  
  2126.     /*
  2127.      * New range spec
  2128.      * If there is enough space, reuse it; oth. allocate enough
  2129.      * (amount allocated never shrinks)
  2130.      */
  2131.     len = max(strlen(range_spec) + 1, STR_ALLOC);
  2132.     if (rh->range_str != NULL  && len > rh->range_str_alloc) {
  2133.         free(rh->range_str);
  2134.         rh->range_str = (char *) malloc((unsigned) len);
  2135.         rh->range_str_alloc = len;
  2136.     }
  2137.     else if (rh->range_str == NULL) {
  2138.         rh->range_str = (char *) malloc((unsigned) len);
  2139.         rh->range_str_alloc = len;
  2140.     }
  2141.     (void) strcpy(rh->range_str, range_spec);
  2142.     if (rh->range != NULL)
  2143.         free((char *) rh->range);
  2144.     rh->range = NULL;
  2145.  
  2146.     p = range_spec;
  2147.     while (1) {
  2148.         lval = getnum(&p, <ype);
  2149.         if (ltype == BAD_T) {
  2150.             (void) fprintf(stderr, "range: bad first number\n");
  2151.             *rh->range_str = '\0';        /* invalidate */
  2152.             return(-1);
  2153.         }
  2154.  
  2155.         if (rh->range == NULL) {
  2156.             smalloc(r, struct range *, sizeof(struct range));
  2157.             rh->nranges = 1;
  2158.         }
  2159.         else {
  2160.             len = sizeof(struct range) * ++(rh->nranges);
  2161.             srealloc(r, struct range *, (char *) rh->range, len);
  2162.         }
  2163.         rh->range = r;
  2164.         r += rh->nranges - 1;        /* point to new one */
  2165.         r->hflag = r->lflag = 0;
  2166.         r->high = r->low = 0;
  2167.  
  2168.         /*
  2169.          * If ltype != NUM_T there is no lval
  2170.          */
  2171.         if (ltype == NUM_T) {
  2172.             r->lflag = 1;
  2173.             r->low = lval;
  2174.         }
  2175.  
  2176.         switch (*p) {
  2177.         case ',':            /* single number */
  2178.             r->hflag = 1;
  2179.             r->high = lval;
  2180.             p++;
  2181.             continue;
  2182.  
  2183.         case '\0':            /* single number at end */
  2184.             r->hflag = 1;
  2185.             r->high = lval;
  2186.             return(0);
  2187.  
  2188.         case ':':
  2189.             p++;
  2190.             if (*p == '\0')        /* no rval */
  2191.                 return(0);
  2192.             if (*p == ',') {    /* no rval */
  2193.                 p++;
  2194.                 break;
  2195.             }
  2196.  
  2197.             rval = getnum(&p, &rtype);
  2198.             if (rtype == BAD_T) {
  2199.                 (void) fprintf(stderr, "range: bad second number\n");
  2200.                 *rh->range_str = '\0';
  2201.                 return(-1);
  2202.             }
  2203.  
  2204.             if (lval > rval) {
  2205.                 (void) fprintf(stderr, "range: values reversed\n");
  2206.                 *rh->range_str = '\0';    /* invalidate */
  2207.                 return(-1);
  2208.             }
  2209.             r->hflag = 1;
  2210.             r->high = rval;
  2211.             if (*p == '\0')
  2212.                 return(0);
  2213.             if (*p == ',')
  2214.                 p++;
  2215.             break;
  2216.  
  2217.         default:
  2218.             (void) fprintf(stderr, "range: bad character\n");
  2219.             *rh->range_str = '\0';        /* invalidate */
  2220.             return(-1);
  2221.         }
  2222.     }
  2223. }
  2224.  
  2225. static
  2226. getnum(pp, type)
  2227. char **pp;
  2228. int *type;
  2229. {
  2230.     register int sign, val;
  2231.     register char *p;
  2232.  
  2233.     p = *pp;
  2234.     if (!isdigit(*p) && *p != '-') {
  2235.         if (*p == SEP_CHAR)
  2236.             *type = SEP_T;
  2237.         else
  2238.             *type = BAD_T;
  2239.         return(0);
  2240.     }
  2241.     sign = 1;
  2242.     if (*p == '-') {
  2243.         sign = -1;
  2244.         p++;
  2245.     }
  2246.     if (!isdigit(*p)) {
  2247.         *type = BAD_T;
  2248.         return(0);
  2249.     }
  2250.     for (val = 0; isdigit(*p) && *p != '\0'; p++)
  2251.         val = val * 10 + *p - '0';
  2252.     if (*p != '\0' && *p != ',' && *p != SEP_CHAR) {
  2253.         *type = BAD_T;
  2254.         return(0);
  2255.     }
  2256.     *pp = p;
  2257.     *type = NUM_T;
  2258.     return(sign * val);
  2259. }
  2260.  
  2261. SHAR_EOF
  2262. #    End of shell archive
  2263. exit 0
  2264. -- 
  2265. Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
  2266. Have five nice days.
  2267.